package com.ylsk.common.utils;

import com.ylsk.common.utils.spring.SpringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.concurrent.TimeUnit;

/**
 * 生成流水线单号格式:业务码+yyyyMMdd+数字
 * 例如: LSH2022102700001
 *
 * @author YANGsy
 */
@Component
public class SerialNoUtils {
    /**
     * 项目报备
     */

    public static final String SGBB_ORDER = "SGBB-";
    /**
     * 施工检查
     */

    public static final String SGJC_ORDER = "SGJC-";
    /**
     * 施工问题
     */

    public static final String SGWT_ORDER = "SGWT-";

    /**
     * 占道报备
     */

    public static final String ZDBB_ORDER = "ZDBB-";
    /**
     * 序号长度
     */
    public static final int SORT_LENGTH = 3;


    private static StringRedisTemplate redisTemplate;

    private static RedissonClient redissonClient;
    static {
        redisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
        redissonClient = SpringUtils.getBean(RedissonClient.class);
    }


    /**
     * 生成17位唯一流水号，"LSH"+yyyyMMdd+6位数字
     * 6位数字，如：000001
     *
     * @param bizCode 业务码
     * @return
     */
    public String generateSerialNo(String bizCode) {
        //定义需要返回的流水号
        String serialNo = null;
        //先查询到今天的日期，格式:"yyyyMMdd"
        String todayDate = new SimpleDateFormat("yyyyMMdd")
                .format(new Date());
        //固定字母前缀 拼接 今天日期，组成新的完整的前缀，也就是缓存的key
        String cacheKey = bizCode + todayDate;
        RLock lock = redissonClient.getLock("SerialNoLock" + cacheKey);
        //通过key查询缓存有没有num数据,没有赋初始值并设置过期时间
        try {
            lock.lock();
            if (Boolean.FALSE.equals(redisTemplate.hasKey(cacheKey))) {
                redisTemplate.opsForValue().set(cacheKey, "0", getSeconds(), TimeUnit.SECONDS);
            }
            //再通过key查询缓存有没有num数据，缓存操作根据自身项目封装工具类
            String value = redisTemplate.opsForValue().get(cacheKey);
            Long codeNum = null;
            //如果缓存查询有值，数值+1，再赋值给下一个流水号
            if (null != value) {
                codeNum = Long.parseLong(value) + 1L;
            } else {
                //如果缓存查询没值，直接赋值为1
                codeNum = 1L;
            }
            //流水号 = 缓存key + 拼接的数值 = 前缀 + 日期 + 拼接的数值
            serialNo = getCodeOfSix(cacheKey, codeNum.intValue());
            //设置缓存，调用此方法，会自动将key所对应的value+1，保存时长：今天剩余的时间
            redisTemplate.opsForValue().increment(cacheKey, 1);
        } finally {
            lock.unlock();
        }
        return serialNo;
    }


    /**
     * 将数值拼接成对应的位数
     *
     * @param prefix 前缀："LSH"+yyyyMMdd
     * @param nowNum 当前要生成的数字
     * @return 拼接好的流水号
     */
    private String getCodeOfSix(String prefix, int nowNum) {

        //需要拼接的数字
        StringBuilder numSb = new StringBuilder(prefix);
        //补零
        int num = SORT_LENGTH - String.valueOf(nowNum).length();
        for (int i = 0; i < num; i++) {
            numSb.append("0");
        }
        numSb.append(nowNum);
        return numSb.toString();
    }


    /**
     * 获取当天结束还剩余多少秒
     *
     * @return
     */
    private static int getSeconds() {
        //获取今天当前时间
        Calendar curDate = Calendar.getInstance();
        //获取明天凌晨0点的日期
        Calendar tomorrowDate = new GregorianCalendar(
                curDate.get(Calendar.YEAR),
                curDate.get(Calendar.MONTH),
                curDate.get(Calendar.DATE) + 1,
                0, 0, 0);
        //返回 明天凌晨0点 和 今天当前时间 的差值（秒数）+1000(防止提前失效)
        return (int) (tomorrowDate.getTimeInMillis() - curDate.getTimeInMillis()) / 1000 + 1000;
    }

}
